package yxy.game.pm2.module;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.joda.time.DateTime;

import org.redisson.api.RSet;
import yxy.apple.util.concurrent.ThreadPoolManager;
import yxy.apple.util.redisson.RedisConnection;
import yxy.apple.util.redisson.RedisException;
import yxy.cherry.data.DataCenter;
import yxy.cherry.data.bean.equipdata;
import yxy.cherry.data.bean.item;
import yxy.cherry.data.bean.playerlvcost;
import yxy.cherry.data.bean.vipdata;
import yxy.cherry.data.string.GameString;
import yxy.cherry.game.base.redis.RedisManager;
import yxy.cherry.game.base.redis.RedisType;
import yxy.game.pm2.bean.*;
import yxy.game.pm2.bean.currency.CurrencyAdditionException;
import yxy.game.pm2.bean.currency.CurrencyGroup;
import yxy.game.pm2.bean.currency.CurrencyNotEnoughException;
import yxy.game.pm2.bean.currency.CurrencyType;
import yxy.game.pm2.bean.mq.CCReason;
import yxy.game.pm2.bean.pubsub.CurrencyPublisher;
import yxy.game.pm2.bean.pubsub.PlayerLevelPublisher;
import yxy.game.pm2.bean.task.TaskType;
import yxy.game.pm2.message.PackageMessage;
import yxy.game.pm2.module.bandit.BanditModule;
import yxy.game.pm2.module.hero.HeroHandle;
import yxy.game.pm2.module.mail.MailHandle;
import yxy.game.pm2.module.mail.MailTypeEnum;
import yxy.game.pm2.module.playerCommand.PlayerCommandModule;
import yxy.game.pm2.module.stat.StatHandle;
import yxy.game.pm2.module.task.TaskHandle;

public class CurrencyHandle {

    public Collection<PackageMessage.Currency> allPoint(String playerCode, Boolean isNewPlayer) {
        ArrayList<PackageMessage.Currency> list = new ArrayList<>();

        for (CurrencyType type : CurrencyType.values()) {
            if (type.getGroup().equals(CurrencyGroup.POINT)) {
                long number = Currency.number(playerCode, type, 0, isNewPlayer);
                PackageMessage.Currency.Builder builder = PackageMessage.Currency.newBuilder();
                builder.setType(type.getNumber());
                builder.setNumber(number);
                list.add(builder.build());
            }
        }

        return list;
    }

    public Collection<PackageMessage.Item> allSign(String playerCode) {
        ArrayList<PackageMessage.Item> list = new ArrayList<>();
        for (CurrencyType type : CurrencyType.values()) {
            if (type.getGroup().equals(CurrencyGroup.SIGN)) {
                Set<String> codes = Sign.all(playerCode, type);
                for (String code : codes) {
                    int dataId = Integer.parseInt(code);
                    PackageMessage.Item.Builder builder = PackageMessage.Item.newBuilder();
                    builder.setDataId(dataId);
                    builder.setNumber(1);
                    list.add(builder.build());
                }
            }
        }

        return list;
    }

    public Collection<PackageMessage.Item> allSign(String playerCode, CurrencyType type) {
        ArrayList<PackageMessage.Item> list = new ArrayList<>();
        Set<String> codes = Sign.all(playerCode, type);
        for (String code : codes) {
            int dataId = Integer.parseInt(code);
            PackageMessage.Item.Builder builder = PackageMessage.Item.newBuilder();
            builder.setType(type.getNumber());
            builder.setDataId(dataId);
            builder.setNumber(1);
            list.add(builder.build());
        }
        return list;
    }

    public Collection<PackageMessage.Item> packItems(Currency... currencies) {
        return packItems(Arrays.asList(currencies));
    }

    /**
     * 根据通货封装Item消息体
     */
    public Collection<PackageMessage.Item> packItems(Collection<Currency> currencies) {
        ArrayList<PackageMessage.Item> items = new ArrayList<>();
        for (Currency currency : currencies) {
            if (currency == null) {
                continue;
            }
            items.add(packItem(currency));
        }
        return items;
    }

    public PackageMessage.Item packItem(Currency currency) {
        PackageMessage.Item.Builder builder = PackageMessage.Item.newBuilder();
        builder.setType(currency.getType().getNumber());
        builder.setDataId(currency.getDataId());
        builder.setNumber(currency.getNumber());
        return builder.build();
    }

    public Collection<PackageMessage.Currency> packPoints(Currency... currencies) {
        return packPoints(Arrays.asList(currencies));
    }

    /**
     * 根据点数封装Currency消息体
     */
    public Collection<PackageMessage.Currency> packPoints(Collection<Currency> currencies) {
        ArrayList<PackageMessage.Currency> points = new ArrayList<>();
        for (Currency currency : currencies) {
            points.add(packPoint(currency));
        }
        return points;
    }

    public PackageMessage.Currency packPoint(Currency currency) {
        PackageMessage.Currency.Builder builder = PackageMessage.Currency.newBuilder();
        builder.setType(currency.getType().getNumber());
        builder.setNumber(currency.getNumber());
        return builder.build();
    }

    /**
     * 增加代币 并通知
     */
    public void addCurrency(String playerCode, CCReason reason, Currency... currencies) {
        if (currencies == null || currencies.length <= 0) {
            return;
        }

        ArrayList<Currency> list = new ArrayList<>();
        for (Currency currency : currencies) {
            check(playerCode, currency);//资源校验
            list.add(currency);
        }
        addCurrency(playerCode, reason, list);
    }

    /**
     * 增加代币 并通知
     */
    public void addCurrency(String playerCode, CCReason reason, Collection<Currency> currencies) {
        if (currencies == null || currencies.size() <= 0) {
            return;
        }
        ThreadPoolManager.getInstance().execute(() -> {
            try {
                Set<CurrencyType> notifyTypes = new HashSet<>();
                Collection<Currency> merge = Currency.merge(currencies);
                for (Currency currency : merge) {
                    //兵符或魂玉是否达到背包上限，达到就放邮件
                    if (isSoulPickedTroopsMaxNum(playerCode, currency)) continue;
                    _addCurrency(playerCode, reason, currency);
                    notifyTypes.add(currency.getType());
                    handleCurrency(playerCode, currency);
                }
                new NotificationHandle().notifyPoint(playerCode, notifyTypes);
                if (notifyTypes.contains(CurrencyType.CT_主公经验)) {
                    checkPlayerLevel(playerCode);
                }
                if (notifyTypes.contains(CurrencyType.CT_VIP经验)) {
                    checkVipLevel(playerCode);
                }
                StatHandle.stat_资源增加(playerCode, reason, merge);

                CurrencyPublisher.getInstance().publishAdd(playerCode, merge);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }


    public Boolean isSoulPickedTroopsMaxNum(String playerCode, Currency currency) {
        if (currency == null || currency.getNumber() < 0) {
            return false;
        }

        //todo 如果是魂玉，判断背包数量是否达到最大值，并放进邮件
        if ((currency.getType().getGroup().equals(CurrencyGroup.SOUL))) {
            if (Soul.all(playerCode).size() > Soul.maxNum) {
                Collection<Currency> rewards = new ArrayList<>();
                rewards.add(currency);
                String title = GameString.魂玉背包上限_title().getText();
                String content = GameString.魂玉背包上限_msg("" + Soul.maxNum, "" + Soul.all(playerCode).size()).getText();
                new MailHandle().sendMail(playerCode, MailTypeEnum.Mail, title, content, CCReason.魂玉背包已满, rewards);
                return true;
            }
        }

        //todo 如果是兵符，判断背包数量是否达到最大值，并放进邮件
        if ((currency.getType().getGroup().equals(CurrencyGroup.PickedTroopsSlot))) {
            if (PickedTroopsSlot.all(playerCode).size() > PickedTroopsSlot.maxNum) {
                Collection<Currency> rewards = new ArrayList<>();
                rewards.add(currency);
                String title = GameString.兵符背包上限_title().getText();
                String content = GameString.兵符背包上限_msg("" + PickedTroopsSlot.maxNum, "" + PickedTroopsSlot.all(playerCode).size()).getText();
                new MailHandle().sendMail(playerCode, MailTypeEnum.Mail, title, content, CCReason.兵符背包已满, rewards);
                return true;
            }
        }

        return false;
    }


    private void _addCurrency(String playerCode, CCReason reason, Currency currency) throws CurrencyAdditionException {
        try {
            switch (currency.getType()) {
                case CT_情报: {
                    long 已有情报值 = Point.get(playerCode, CurrencyType.CT_情报, false);
                    long upper = BanditModule.get(playerCode).情报upper();
                    long number = currency.getNumber();
                    if (已有情报值 + currency.getNumber() > upper) {
                        number = upper - 已有情报值;
                        if (number <= 0) {
                            currency.setNumber(0);
                            return;
                        }
                        currency.setNumber(number);
                    }
                    Currency.add(playerCode, currency);
                    break;
                }
                case CT_武将: {
                    new HeroHandle().createHero(playerCode, currency.getDataId(), currency.getNumber(), reason);
                    break;
                }
                default:
                    Currency.add(playerCode, currency);
                    break;
            }
        } catch (RedisException e) {
            e.printStackTrace();
        }
    }

    /**
     * 处理代币相关的内容
     */
    private void handleCurrency(String playerCode, Currency currency) {
        try {
            switch (currency.getType()) {
                case CT_装备: {
                    equipdata data = DataCenter.getData(currency.getDataId(), equipdata.class);
                    TaskHandle.handle(playerCode, TaskType.统计型_背包_装备_品质_数量, currency.getNumber(), data.getQuality());
                }
                break;
                default:
                    break;
            }
        } catch (Exception e) {
        }
    }

    public void cost(String playerCode, CCReason reason, Currency... currencys) throws CurrencyNotEnoughException {
        ArrayList<Currency> list = new ArrayList<>();
        for (Currency currency : currencys) {
            list.add(currency);
        }
        cost(playerCode, reason, list);
    }

    public void cost(String playerCode, CCReason reason, Collection<Currency> costs) throws CurrencyNotEnoughException {
        Currency.minus(playerCode, costs);

        Set<CurrencyType> notifyTypes = new HashSet<>();
        for (Currency currency : costs) {
            notifyTypes.add(currency.getType());
        }
        new NotificationHandle().notifyPoint(playerCode, notifyTypes);

        StatHandle.stat_资源减少(playerCode, reason, costs);

        CurrencyPublisher.getInstance().publishMinus(playerCode, costs);
    }

    /**
     * 检查是否可消费
     */
    public boolean costCheck(String playerCode, Currency... currencys) {
        ArrayList<Currency> list = new ArrayList<>();
        for (Currency currency : currencys) {
            list.add(currency);
        }
        return costCheck(playerCode, list);
    }

    /**
     * 检查是否可消费
     */
    public boolean costCheck(String playerCode, Collection<Currency> costs) {
        try {
            return Currency.checkminus(playerCode, costs);
        } catch (Exception e) {
        }
        return false;
    }

    public void checkPlayerLevel(String playerCode) {
        PlayerBean player = PlayerBean.get(playerCode);
        try {
            if (player.tryLock()) {
                int originalLevel = player.getLevel();
                playerlvcost _nextplayerlvcost = DataCenter.getData(originalLevel + 1, playerlvcost.class);
                if (_nextplayerlvcost == null) {
                    // 已满级不再升级
                    return;
                }
                int level = originalLevel;

                playerlvcost _playerlvcost = DataCenter.getData(level, playerlvcost.class);
                Currency cost = Currency.create(_playerlvcost.getCosttype(), _playerlvcost.getCostid(), _playerlvcost.getCostnum());
                while (costCheck(playerCode, cost)) {
                    level++;
                    cost(playerCode, CCReason.角色升级, cost);
                    player.setLevel(level);

                    //todo 玩家升1级，统御点数加1
                    PlayerCommandModule commandModule = PlayerCommandModule.get(playerCode);
                    commandModule.setPoint(commandModule.getPoint() + 1);
                    commandModule.set(playerCode);

                    TaskHandle.handle(playerCode, TaskType.指标型_玩家_等级_达到, player.getLevel());
                    _nextplayerlvcost = DataCenter.getData(level + 1, playerlvcost.class);
                    if (_nextplayerlvcost == null) {
                        // 已满级不再升级
                        break;
                    }
                    _playerlvcost = DataCenter.getData(player.getLevel(), playerlvcost.class);
                    cost = Currency.create(_playerlvcost.getCosttype(), _playerlvcost.getCostid(), _playerlvcost.getCostnum());
                }

                if (player.getLevel() == originalLevel) {
                    return;
                }

                player.setUpgradeTime(DateTime.now());
                player.set();

                new NotificationHandle().notifyPlayerLevel(playerCode, player.getLevel(), Currency.number(playerCode, CurrencyType.CT_主公经验, 0, false));

                TaskHandle.handle(playerCode, TaskType.指标型_玩家_等级_达到, player.getLevel());

                StatHandle.stat_等级变动(player, player.getLevel() - originalLevel);

//				//世界等级不再进行计算取平均，统一用开服时间和表配置
                PlayerLevelPublisher.getInstance().publish(player, originalLevel);
            }
        } catch (Exception e) {
        } finally {
            player.unlock();
        }
    }

    private void checkVipLevel(String playerCode) {
        PlayerBean player = PlayerBean.get(playerCode);
        try {
            if (player.tryLock()) {
                int viplv = player.getViplevel();
                long vipexp = Currency.number(playerCode, CurrencyType.CT_VIP经验, 0, false);
                vipdata _vipdata = DataCenter.getData(viplv, vipdata.class);
                int lv = viplv;
                while (_vipdata != null && vipexp >= _vipdata.getVipexp()) {
                    _vipdata = DataCenter.getData(lv + 1, vipdata.class);
                    if (_vipdata == null) {
                        break;
                    }
                    lv++;
                }
                if (lv > viplv) {
                    player.setViplevel(lv);
                    player.set();

                    new NotificationHandle().notifyPlayerVipLevel(playerCode, lv);
                }

            }
        } catch (Exception e) {
        } finally {
            player.unlock();
        }

    }

    //数据校验
    private boolean check(String playerCode, Currency currency) {
        RedisConnection connection = RedisManager.getInstance().getConnection(RedisType.REDIS_GAME);
        RSet<String> currencySet = connection.client().getSet("ERROR:CURRENCY");

        CurrencyType type = currency.getType();

        if (type == CurrencyType.CT_装备) {
            //对传入的数据进行校验
            equipdata equipdata = DataCenter.getData(currency.getDataId(), equipdata.class);
            //不存在或数量错误，将错误信息存放在redis
            if (equipdata == null) {
                currencySet.add(type.name() + "_" + currency.getDataId() + "_" + playerCode);
                return false;
            }
        } else if (type == CurrencyType.CT_物品 || type == CurrencyType.CT_玉石 || type == CurrencyType.CT_天书 || type == CurrencyType.CT_碎片) {
            //对传入的数据进行校验
            item item = DataCenter.getData(currency.getDataId(), item.class);
            //不存在或数量错误，将错误信息存放在redis
            if (item == null) {
                currencySet.add(type.name() + "_" + currency.getDataId() + "_" + playerCode);
                return false;
            }
        }
        return true;
    }

}
